from typing import Union, Iterator


class GraphBase:
    def __init__(self) -> None:
        self._ind = None  # type: int

    @property
    def ind(self) -> int:
        return self._ind

    def similar(self, other: "GraphBase") -> bool:
        return self.name == other.name

    def _set_ind(self, ind: int):
        self._ind = ind

    def __str__(self) -> str:
        raise NotImplementedError

    @property
    def name(self) -> str:
        return "GraphBase"


class Point(GraphBase):
    def __init__(self, x: float, y: float) -> None:
        self.x = x  # type: float
        self.y = y  # type: float
        self._ind = None  # type: int

    def __iter__(self) -> list:
        yield from [self.x, self.y]

    def near(self, other: "Point", epi: int = 8) -> bool:
        return abs(self.x - other.x) < epi and abs(self.y - other.y) < epi

    def __str__(self) -> str:
        return "x: " + str(self.x) + " y: " + str(self.y)

    def to_tuple(self) -> tuple:
        return (self.x, self.y)

    def __eq__(self, other: "Point") -> bool:
        return self.x == other.x and self.y == other.y

    @property
    def name(self) -> str:
        return "Point"


class Line(GraphBase):
    def __init__(self, point_1: Point, point_2: Point) -> None:
        self.p1 = point_1  # type: Point
        self.p2 = point_2  # type: Point
        self._ind = None  # type: int

    def __str__(self) -> str:
        return "p1: " + str(self.p1) + "\np2: " + str(self.p2)

    def to_tuple(self) -> tuple:
        return (self.p1, self.p2)

    def near(self, other: "Line") -> bool:
        return (self.p1.near(other.p1) and self.p2.near(other.p2)) or (
            self.p1.near(other.p2) and self.p2.near(other.p1)
        )

    def lay_on_line(self, other: Point) -> bool:
        """
        点在直线上
        """
        return self.p1 == other or self.p2 == other

    @property
    def name(self) -> str:
        return "Line"


class ManagerBase:
    def __init__(self) -> None:
        self._manager = {}  # type: dict[tuple]
        self.lenth = 0

    def __len__(self) -> int:
        return self.lenth

    def __getitem__(self, index: int) -> tuple[int, Point]:
        return self._manager[index]

    def output(self) -> Iterator["GraphBase"]:
        yield from self._manager.values()

    def __iter__(self) -> tuple[int, Point]:
        yield from self._manager.items()

    def append(self, *other: [int, Point], id: int) -> None:
        self.lenth += 1
        other[1]._set_ind(id)
        self._manager[id] = other

    def pop(self, index: int) -> Point:
        self.lenth -= 1
        return self._manager.pop(index)

    def is_near(self, other: Union[Point, Line]) -> int:
        # 先不考虑性能问题
        for key, meta in self._manager.items():
            if other.similar(meta[1]) and meta[1].near(other):
                return key
        return -1
